package ru.elifantiev.cityrouter;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Address;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.TextView;
import ru.elifantiev.cityrouter.infrastructure.adapters.InfiniteAdapter;
import ru.elifantiev.cityrouter.infrastructure.geocoding.Geocoder;
import ru.elifantiev.cityrouter.infrastructure.geocoding.GeocoderFactory;
import ru.elifantiev.cityrouter.infrastructure.geocoding.GeocoderResult;
import ru.elifantiev.cityrouter.infrastructure.geocoding.PagedGeocoder;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;


public class GeoSearchActivity extends ListActivity {

    public static final String SEARCH_QUERY_EXTRA = "searchQuery";
    public static final String SEARCH_RESULT_GEOCODER_RESPONSE = " ru.hippocamp.geocoderSearchResponse";
    public static final String SEARCH_RESULT_LATITUDE = "latitude";
    public static final String SEARCH_RESULT_LONGITUDE = "longiture";

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        String query = getIntent().getStringExtra(SEARCH_QUERY_EXTRA);
        try {
            setListAdapter(new InfiniteGeocoderAdapter(R.layout.address_list_item, query));
        } catch (Exception e) {
            finish();
        }

        setTitle(String.format(getString(R.string.searchResultsTabCaption), query));

        getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
            public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
                Object obj = getListView().getAdapter().getItem(pos);
                if (obj != null && obj instanceof Address) {
                    Intent resultData = new Intent(SEARCH_RESULT_GEOCODER_RESPONSE);
                    resultData.putExtra(SEARCH_RESULT_LATITUDE, ((Address) obj).getLatitude());
                    resultData.putExtra(SEARCH_RESULT_LONGITUDE, ((Address) obj).getLongitude());
                    setResult(1, resultData);
                    finish();
                }
            }

            public void onNothingSelected(AdapterView<?> adapterView) {
            }
        });

    }

    private class AddressArrayAdapter extends BaseAdapter {

        private final String separator = ", ";
        private final int separatorLen = separator.length();
        private final String methodBeginsWith = "get";

        private List<Address> addresses = new ArrayList<Address>();
        private int viewResource;
        private String[] placeItems = new String[]{
                "CountryName", "AdminArea", "SubAdminArea", "Locality", "Thoroughfare"
        };
        private String[] addrItems = new String[]{
                "FeatureName"
        };

        public AddressArrayAdapter(List<Address> addresses, int textItemViewResource) {
            this(textItemViewResource);
            this.addresses = addresses;
        }

        public AddressArrayAdapter(int textItemViewResource) {
            this.viewResource = textItemViewResource;
        }

        public List<Address> getList() {
            return addresses;
        }

        public int getCount() {
            return addresses.size();
        }

        public Object getItem(int i) {
            return addresses.get(i);
        }

        public long getItemId(int i) {
            return i;
        }

        public View getView(int i, View adoptView, ViewGroup parent) {
            Address addr = addresses.get(i);
            View view;

            if (adoptView != null)
                view = adoptView;
            else
                view = LayoutInflater.from(GeoSearchActivity.this).inflate(viewResource, parent, false);

            ((TextView) view.findViewById(R.id.txtPlace)).setText(buildAddressLine(placeItems, addr));
            ((TextView) view.findViewById(R.id.txtAddressLine)).setText(buildAddressLine(addrItems, addr));
            return view;
        }

        private String buildAddressLine(String[] placeItems, Address addr) {
            StringBuffer addrString = new StringBuffer();
            for (int c = 0, l = placeItems.length; c < l; c++) {
                try {
                    String res = (String) Address.class.getMethod(
                            methodBeginsWith + placeItems[c], (Class<?>[])null).invoke(addr, (Object[])null);
                    if (res != null && !res.equals(""))
                        addrString.append(res);
                    else
                        continue;
                } catch (NoSuchMethodException e) {
                    // ignore
                } catch (InvocationTargetException e) {
                    // ignore
                } catch (IllegalAccessException e) {
                    // ignore
                }
                addrString.append(separator);
            }
            String res = addrString.toString();
            if (res.endsWith(separator))
                res = res.substring(0, res.length() - separatorLen);
            return res;
        }
    }

    class InfiniteGeocoderAdapter extends InfiniteAdapter {

        private GeocoderResult lastResult = new GeocoderResult();
        private String query;
        private Geocoder geocoder;
        private final int PAGE_SIZE = 10;
        private int page = 0;
        private Animation rotation;

        @Override
        protected boolean loadItems() {
            if (geocoder instanceof PagedGeocoder) {
                ((PagedGeocoder) geocoder).setResponseBegin(page * PAGE_SIZE);
            }

            try {
                lastResult = geocoder.getFromLocationName(query, PAGE_SIZE);
            } catch (Exception e) {
                return false;
            }

            return page * PAGE_SIZE + lastResult.size() < lastResult.getTotalResults();
        }

        @Override
        protected View getLoadingPlaceholder(ViewGroup parent) {
            View view = getLayoutInflater().inflate(R.layout.loading_list_item, null);
            view.findViewById(R.id.loadingImage).startAnimation(rotation);
            return view;
        }

        @Override
        protected void appendLoadedItems() {
            if (page == 0 && lastResult.size() == 0) {
                AlertDialog.Builder builder = new AlertDialog.Builder(GeoSearchActivity.this);
                builder.setMessage(getString(R.string.emptySearchResults))
                        .setCancelable(false)
                        .setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                GeoSearchActivity.this.finish();
                            }
                        });
                builder.create().show();
            }
            else {
                page++; // increment page for next run
                ((AddressArrayAdapter) getWrappedAdapter()).getList().addAll(lastResult);
            }
        }

        public InfiniteGeocoderAdapter(int viewResource, String query) throws IllegalArgumentException {
            super(new AddressArrayAdapter(viewResource));
            this.geocoder = GeocoderFactory.build(GeoSearchActivity.this);
            this.query = query;
            rotation = new RotateAnimation(
                    0f,
                    360f,
                    Animation.RELATIVE_TO_SELF,
                    0.5f,
                    Animation.RELATIVE_TO_SELF,
                    0.5f);
            rotation.setDuration(1200);
            rotation.setInterpolator(new LinearInterpolator());
            rotation.setRepeatMode(Animation.RESTART);
            rotation.setRepeatCount(Animation.INFINITE);
        }
    }

}